package com.jack.completablefuture;

import java.util.Arrays;
import java.util.List;
import java.util.concurrent.*;

import static java.util.stream.Collectors.toList;

/**
 * 并行——使用流还是CompletableFuture？
 *      如果你进行的是计算密集型的操作，并且没有IO，那么推荐使用stream接口
 *      如果你并行的工作单元涉及等待IO的操作（包括网络连接等待），那么使用CompletableFuture灵活性更好
 * Created by zhang_j on 2019/11/26
 */
public class API {
    private final Executor executor =
            Executors.newFixedThreadPool(Math.min(shops.size(), 100),
                    r -> {
                            Thread t = new Thread(r);
                            t.setDaemon(true);
                            return t;
                    });

    private static List<Shop> shops = Arrays.asList(new Shop("BestPrice"),
            new Shop("LetsSaveBig"),
            new Shop("MyFavoriteShop"),
            new Shop("BuyItAll"),
            new Shop("FifthShop"),
            new Shop("SixthShop"),
            new Shop("SeventhShop"));

    public static void main(String[] args) {
        long start = System.nanoTime();
        System.out.println(findPrices("myPhone27S"));
        long duration = (System.nanoTime() - start) / 1_000_000;
        System.out.println("Done in " + duration + " msecs");
    }

    /**
     * CompletableFuture类中的join方法和Future接口中的get有相同的含义，并且也声明在Future接口中
     * 他们唯一的不同就是join不会抛出任何检测到的异常。
     * 2000+ms
     * 当把shop的数量扩大时发现使用parallelStream和CompletableFuture的时间基本是一样的。
     * 原因：因为可以并行运行（通用线程池处于可用状态的ForkJoinPool）一般是四个线程
     * @param product
     * @return
     */
    public static List<String> findPrices(String product){
        List<CompletableFuture<String>> priceFutures =
                shops.stream()
                        .map(shop -> CompletableFuture.supplyAsync(
                                () -> String.format("%s price is %.2f",
                                        shop.name, shop.getPrice(product))))
                        .collect(toList());
        return priceFutures.stream()
                .map(CompletableFuture::join)
                .collect(toList());
    }

    /**
     *  这里使用了parallelStream，肯定要比使用stream要快不少，但这样真的好吗
     *  stream用了4000+ms，parallelStream用了1000+ms
     *  问题：为什么stream的延迟特性会引起顺序执行，以及如何避免
     * @param product
     * @return
     */
//    public static List<String> findPrices(String product) {
//
//        return shops.parallelStream()
//                .map(shop -> String.format("%s price is %.2f",
//                        shop.name, shop.getPrice(product)))
//                .collect(toList());
//    }

//    public static void main(String[] args) {
//        Shop shop = new Shop("BestShop");
//        long start = System.nanoTime();
//        Future<Double> futurePrice = shop.getPriceAsync("MySingleton favorite product");
//        long invocationTime = ((System.nanoTime() - start) / 1_000_000);
//        System.out.println("Invocation returned after " + invocationTime
//                + " msecs");
//
//        //查询其他商店
//
//        //在计算商店价格的同时
//        try{
//            Double price = futurePrice.get();
//            System.out.printf("Price is %.2f%n", price);
//        }catch (Exception e){
//            throw new RuntimeException(e);
//        }
//
//        long retrievalTime = ((System.nanoTime() - start) / 1_000_000);
//        System.out.println("Price returned after " + retrievalTime + " msecs");
//    }
}
